package org.osomit.sacct.connector.socket.impl;

import org.osomit.sacct.connector.socket.iface.SocketConnector;
import org.osomit.sacct.entity.Response;
import org.osomit.sacct.entity.Request;
import org.osomit.sacct.entity.SecureResponse;
import org.osomit.sacct.entity.SecureRequest;
import org.osomit.sacct.io.iface.ClientReaderWriter;
import org.osomit.sacct.SAcctException;

import java.net.*;
import java.io.*;

/**
 * @author Jian Fang (John.Jian.Fang@gmail.com)
 *
 * Date: Jun 29, 2009
 */
public class SocketConnectorImpl implements SocketConnector {
    private static final String ERROR_UNKNOWN_HOST = "Unknown host ";
    private static final String ERROR_SOCKET_EXCEPTION = "Socket Exception: ";
    private static final String ERROR_IO_EXCEPTION = "Socket Connector IOException: ";
    private static final String ERROR_GENERAL_SOCLET_CONNECTOR_EXCEPTION = "Socket Connector Exception: ";

    private String serverHost;
    
    private int serverPort;

    private ClientReaderWriter clientReaderWriter;

    private Socket clientSocket;

    private OutputStream socketWriter;

    private BufferedReader socketReader;

    public SocketConnectorImpl(String serverHost, int serverPort) {
        this.serverHost = serverHost;
        this.serverPort = serverPort;
    }

    public OutputStream getSocketWriter() {
        return socketWriter;
    }

    public void setSocketWriter(OutputStream socketWriter) {
        this.socketWriter = socketWriter;
    }

    public BufferedReader getSocketReader() {
        return socketReader;
    }

    public void setSocketReader(BufferedReader socketReader) {
        this.socketReader = socketReader;
    }

    public Socket getClientSocket() {
        return clientSocket;
    }

    public void setClientSocket(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    public String getServerHost() {
        return serverHost;
    }

    public void setServerHost(String serverHost) {
        this.serverHost = serverHost;
    }

    public int getServerPort() {
        return serverPort;
    }

    public void setServerPort(int serverPort) {
        this.serverPort = serverPort;
    }

    public ClientReaderWriter getClientReaderWriter() {
        return clientReaderWriter;
    }

    public void setClientReaderWriter(ClientReaderWriter clientReaderWriter) {
        this.clientReaderWriter = clientReaderWriter;
    }

    public void connect(){
        try {
            InetAddress addr =InetAddress.getByName(this.serverHost);
            SocketAddress sockaddr = new InetSocketAddress(addr, this.serverPort);

            // Create an unbound socket
            this.clientSocket = new Socket();

            // This method will block no more than timeoutMs.
            // If the timeout occurs, SocketTimeoutException is thrown.
            int timeoutMs = 15 * 1000; // 15 seconds
            this.clientSocket.setSoTimeout(timeoutMs);
            this.clientSocket.connect(sockaddr);
            this.socketWriter = this.clientSocket.getOutputStream();
            this.socketReader = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));

        } catch (UnknownHostException e) {
            throw new SAcctException(ERROR_UNKNOWN_HOST + this.serverHost + ":" + this.serverPort + ", " + e.getMessage());
        } catch (SocketException e) {
            throw new SAcctException(ERROR_SOCKET_EXCEPTION + e.getMessage());
        } catch (IOException e) {
            throw new SAcctException(ERROR_IO_EXCEPTION + e.getMessage());
        } catch (Exception e){
            throw new SAcctException(ERROR_GENERAL_SOCLET_CONNECTOR_EXCEPTION + e.getMessage());
        }

    }

    public void disconnect() {
        try {
            this.socketWriter.close();
            this.socketReader.close();
            this.clientSocket.close();
        } catch (IOException e) {            
        }
    }

    public SecureResponse submit(SecureRequest request) {
        this.clientReaderWriter.write(this.socketWriter, request);
        SecureResponse resp = this.clientReaderWriter.read(this.socketReader);

        return resp;
    }
}
